//////////////////////////////////////////////
// Manager.h
// 
//////////////////////////////////////////////

/// Defines / Macros -------------------------

#pragma once

/// Includes ---------------------------------

// nkGraphics
#include "../Log/LogManager.h"

#include "../System.h"

// nkMemory
#include <NilkinsMemory/Containers/StringView.h>

#include <NilkinsMemory/Pointers/UniquePtr.h>

// Standards
#include <string>
#include <unordered_map>

/// Class ------------------------------------

namespace nkGraphics
{
	template <typename T>
	class Manager
	{
		public :

			// Memory management
			T* createOrRetrieve (nkMemory::StringView name)
			{
				// Attempt to search
				typename std::unordered_map<std::string, nkMemory::UniquePtr<T>>::const_iterator searchResult = _memory.find(name) ;

				if (searchResult != _memory.end())
					return searchResult->second.get() ;

				// Create then
				nkMemory::UniquePtr<T> resource = T::create(_system) ;
				T* result = resource.get() ;
				result->setName(name) ;

				_setupResource(result) ;

				_memory.emplace(name, std::move(resource)) ;

				return result ;
			}

			T* record (nkMemory::StringView name, nkMemory::UniquePtr<T> resource)
			{
				typename std::unordered_map<std::string, nkMemory::UniquePtr<T>>::const_iterator searchResult = _memory.find(name) ;

				if (searchResult != _memory.end())
					_system->getLogManager()->log("Trying to record from outside over already registered resource named '" + std::string(name) + "'. Beware the existing resource will be erased and freed.", "Manager") ;

				// Register into our memory
				T* result = resource.get() ;
				_memory.emplace(name, std::move(resource)) ;

				return result ;
			}

			T* get (nkMemory::StringView name) const 
			{
				typename std::unordered_map<std::string, nkMemory::UniquePtr<T>>::const_iterator searchResult = _memory.find(name) ;

				if (searchResult != _memory.end())
					return searchResult->second.get() ;

				// Pas trouvé
				return nullptr ;
			}

			T* getByIndex (unsigned int index) const
			{
				if (index >= _memory.size())
					return nullptr ;

				// Loop
				typename std::unordered_map<std::string, nkMemory::UniquePtr<T>>::const_iterator it = _memory.begin() ;
				typename std::unordered_map<std::string, nkMemory::UniquePtr<T>>::const_iterator end = _memory.end() ;
				unsigned int i = 0 ;

				for ( ; it != end ; ++it)
				{
					if (!it->second->getHidden())
					{
						if (i++ == index)
							return it->second.get() ;
					}
				}

				// Not found
				return nullptr ;
			}

			void rename (nkMemory::StringView currentName, nkMemory::StringView newName)
			{
				// Find back
				typename std::unordered_map<std::string, nkMemory::UniquePtr<T>>::iterator searchResult = _memory.find(currentName) ;

				if (searchResult != _memory.end())
				{
					nkMemory::UniquePtr<T> toMove = std::move(searchResult->second) ;
					toMove->setName(newName) ;

					// Et chez nous
					_memory.erase(searchResult) ;
					_memory.emplace(newName, std::move(toMove)) ;
				}
			}

			void erase (nkMemory::StringView name)
			{
				typename std::unordered_map<std::string, nkMemory::UniquePtr<T>>::const_iterator searchResult = _memory.find(name) ;

				if (searchResult != _memory.end())
					_memory.erase(searchResult) ;
			}

			nkMemory::UniquePtr<T> relinquish (nkMemory::StringView name)
			{
				// Check if exists
				typename std::unordered_map<std::string, nkMemory::UniquePtr<T>>::iterator searchResult = _memory.find(name) ;

				if (searchResult != _memory.end())
				{
					nkMemory::UniquePtr<T> result = std::move(searchResult->second) ;
					_memory.erase(searchResult) ;

					return result ;
				}

				return nullptr ;
			}

		protected :

			// Constructors
			Manager (bool isSingleton) noexcept
			:	Manager (System::getInstance())
			{
				// Nothing to do
			}

			Manager (System* system) noexcept
			:	_memory (),
				_system (system)
			{
				// Nothing to do
			}

			Manager (const Manager&) = delete ;

			// Customization
			virtual void _setupResource (T* resource)
			{
				// Nothing to do
			}

			// Operators
			Manager& operator= (const Manager&) = delete ;

		protected :

			// Attributes
			std::unordered_map<std::string, nkMemory::UniquePtr<T>> _memory ;
			System* _system ;
	} ;
}